#include "AbstractMappingWindow.h"
#include "MappingNodeHandler.h"
#include "xMappingNodeTypeLibrary.h"
#include "NodeItem.h"
#include "NodeInPort.h"
#include "NodeOutPort.h"
#include "DIVA_Manager.h"
#include "DiagramScene.h"
#include "AbstractManager.h"
#include "vmLibrary.h"
#include "xInMappingPort.h"

#include <QtGui/QApplication>
#include <QtGui/QDesktopWidget>
#include <QtCore/QString>
#include <QtGui/QGraphicsView>
#include <QtGui/QPushButton>
#include <QtCore/QFile>
#include <QtCore/QSignalMapper>
#include <QtGui/QMessageBox>
#include <QtCore/QDir>
#include <QtGui/QImageReader>

#include <vector>
#include <fstream>
#include <cassert>

#include <QtCore/QtDebug>

using namespace std;

AbstractMappingWindow::AbstractMappingWindow(QWidget *parent, Qt::WFlags flags)
	: QMainWindow(parent, flags)
{	
	ui.setupUi(this);
	this->_currentScene = NULL;
	this->_currentView = NULL;
	this->_currentManager = NULL;
	this->_currentSelectedNode = NULL;
	this->_timerId = -1;
}
AbstractMappingWindow::~AbstractMappingWindow()
{
	if ( _currentScene ){
		delete _currentScene;
	}
	if ( _currentView ) {
		delete _currentView;
	}
	if ( _currentManager ) {
		delete _currentManager;
	}
}


NodeItem * AbstractMappingWindow::addMappingNode(QString mappingNodeName)
{
	// 這裡不能使用QString::toStdString()，mappingNodeName會無法正確轉成std::string
	NodeItem *mappingNode = _currentScene->getManager()->createNode(mappingNodeName.toLocal8Bit().data());
	this->_currentScene->addItem(mappingNode);

	// 將mapping node放在畫布的中央
	// TODO: 這裡還沒解決重疊的問題
	mappingNode->setPos(_currentView->viewport()->size().width()/2, _currentView->viewport()->size().height()/2 - mappingNode->height());	

	return mappingNode;
}


void AbstractMappingWindow::init()
{
	
	_canvasBackgroundColor.reset( new QColor("#333333"));

	initMenuBar();
	initPortTabWidget();
	initEditArea();			// 初始化dialog左下方讓使用者拉mapping的QGaphicsView跟QGraphicsScene
	initVMButtonTab();			// 初始化dialog上方VM按鈕的Tab
	setConnection();			// 設定slot跟signal的connection
	setStyle();					// 設定dialog外觀
}

// Canvas採用分頁的方式，一開始dialog pop出來的時候只有一個 Main tab
// Main tab是一個QWidget，內有供使用者拉mapping的畫布，下方有QPushButton
void AbstractMappingWindow::initEditArea()
{
	ui.mappingTreeTabWidget->clear();
	initCanvas();
	createOtherWidget();
}


// 初始化dialog上方VM 按鈕的 TabWidget
void AbstractMappingWindow::initVMButtonTab()
{
	vector<string> *catrgorys;

	ui.nodeTabWidget->clear();											// 清除所有tabWidget上的pages
	catrgorys = MappingNodeHandler::getInstance()->getCategorys();		// 呼叫MappingNodeHandler的getCategorys()來取得所有VM的種類

	// insert tabs into tabWidget
	for (int i = 0; i < catrgorys->size() ; ++i ) {

		if ( catrgorys->at(i) != "tools" && catrgorys->at(i) != "gate") {	// 在拉mapping node時不需要composite tools跟gate類別裡面的工具
			QWidget *tab = new QWidget();
			QHBoxLayout *layout = new QHBoxLayout(tab);				// 新增一個Layout來排版每個tab裡面的button
			
			layout->setSpacing(0);
			layout->setContentsMargins(10,0,0,0);

			setupVMButton(layout, catrgorys->at(i));			// 設定VM按鈕的icon與大小		

			layout->setSizeConstraint(QLayout::SetFixedSize);
			tab->setLayout(layout);

			ui.nodeTabWidget->insertTab(i, tab, catrgorys->at(i).c_str());	

		}
	}
}

// 設定每個VM Button的icon跟名稱，VM Button的icon圖檔放在vm_icon資料夾底下
// 為了防止VM名稱過長，所以只擷取 "_" 前面的字串當名稱
void AbstractMappingWindow::setupVMButton(QHBoxLayout *layout, string category)
{
	vector<xMappingNodeTypeDescriptor*> *mappingNodeList = MappingNodeHandler::getInstance()->getNodeList(category);
	QDir iconDir( ":/resource/vm_icon" );

	for (int i=0; i<mappingNodeList->size(); ++i)
	{
		QVBoxLayout *vmIconLayout = new QVBoxLayout();
		QPushButton *button = new QPushButton();
		QString buttonName = QString::fromStdString( mappingNodeList->at(i)->_name );
		button->setObjectName( buttonName );

		// set button icon
		button->setIconSize(QSize(VM_BUTTON_WIDTH,VM_BUTTON_HEIGHT));
		QFile iconFile = iconDir.filePath( QString( "%1.png" ).arg( buttonName ) );

		// 載入與MappingNode名稱相同的icon圖檔，如果沒有該圖檔則將button icon設為 example_icon.png
		if( iconFile.open(QIODevice::ReadOnly) ) {
			QImageReader iin( &iconFile );
			button->setIcon( QIcon( QPixmap::fromImageReader( &iin ) ) );
		} else {
			button->setIcon( QIcon( ":/resource/vm_icon/example_icon.png" ) );
		}
		button->setFlat(true);
		QString* tipText = new QString(mappingNodeList->at(i)->_name.c_str());
		tipText->replace('_'," ");
		button->setToolTip(tipText->toLower());

		// 設定button被按下之後的動作(用signal & slot)
		QSignalMapper *signalMapper = new QSignalMapper(this);
		signalMapper->setMapping(button, mappingNodeList->at(i)->_name.c_str());

		connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));
		connect(button, SIGNAL(pressed()),this, SLOT(timerStart()));
		connect(button, SIGNAL(released()),this, SLOT(timerEnd()));
		connect(signalMapper, SIGNAL(mapped(QString)), this, SLOT(addMappingNode(QString)));

		// set button text
		QString* text = new QString(mappingNodeList->at(i)->_name.c_str());
		text->replace('_',"\n");

		QLabel *label = new QLabel();

		label->setText(text->toLower());
		label->setAlignment(Qt::AlignHCenter);


		// set layout attributes
		vmIconLayout->addWidget(button);
		vmIconLayout->addWidget(label);
		
		vmIconLayout->setSpacing(0);
		vmIconLayout->setContentsMargins(0,0,0,10);
		vmIconLayout->setAlignment(Qt::AlignHCenter);

		layout->addLayout(vmIconLayout);

	}	
}

// 設定signal跟slot
void AbstractMappingWindow::setConnection()
{
	connect( ui.inportTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( updateMappingNode(QTreeWidgetItem* , int)));
	connect( ui.outportTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( updateMappingNode(QTreeWidgetItem* , int)));
	connect( ui.attributesTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( setPortValue(QTreeWidgetItem* , int)));

	connect( ui.inportTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( adjustCheckboxState(QTreeWidgetItem* , int)));
	connect( ui.outportTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( adjustCheckboxState(QTreeWidgetItem* , int)));

	connect( ui.inportTreeWidget, SIGNAL(itemExpanded(QTreeWidgetItem* )), this, SLOT(setExpandedState(QTreeWidgetItem*)));
	connect( ui.inportTreeWidget, SIGNAL(itemCollapsed(QTreeWidgetItem* )), this, SLOT(setExpandedState(QTreeWidgetItem*)));
	connect( ui.outportTreeWidget, SIGNAL(itemExpanded(QTreeWidgetItem* )), this, SLOT(setExpandedState(QTreeWidgetItem*)));
	connect( ui.outportTreeWidget, SIGNAL(itemCollapsed(QTreeWidgetItem* )), this, SLOT(setExpandedState(QTreeWidgetItem*)));
	connect( ui.attributesTreeWidget, SIGNAL(itemExpanded(QTreeWidgetItem* )), this, SLOT(setExpandedState(QTreeWidgetItem*)));
	connect( ui.attributesTreeWidget, SIGNAL(itemCollapsed(QTreeWidgetItem* )), this, SLOT(setExpandedState(QTreeWidgetItem*)));
	connect( ui.mappingTreeTabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
	connect( ui.mappingTreeTabWidget, SIGNAL(currentChanged(int)), this, SLOT(currentTabChange(int)));
}

// 讀取外css檔案來設定dialog外觀
void AbstractMappingWindow::setStyle()
{
	QFile* cssFile = new QFile( ":/resource/style.css" );
	assert(cssFile);
	cssFile->open(QFile::ReadOnly);
	this->setStyleSheet(cssFile->readAll());
	cssFile->close();
	delete cssFile;
}

// 當點選到畫布某一個Mapping Node時，dialog右下方的TabWidget會顯示該MappingNode的資訊
// 右方的TabWidget有三個Tab: attribute, inport, outport。attribute顯示Mapping Node屬性
// 的值(使用者自行輸入)；inport跟outport則是顯示該Node目前有哪些port顯示在畫布上(可透過
// 勾選來開啟或隱藏port)
void AbstractMappingWindow::showAttribute( NodeItem* item )
{
	assert(item);

	// 如果目前被點選到的Mapping Node還是同一個的話右邊的TabWidget不需要重新載入
	// 但是雖然點選到是同一個Node可是 port數目有增減的話(例如math mapping node在inport被連後
	// 會自動在長一個inport出來)，則需要重新載入
	if ( item != this->_currentSelectedNode || item->isPortListChanged()){

		this->_currentSelectedNode = item;		// 將_currentSelectNode設為目前點選到的這個item

		vector<NodeInPort*> inports = _currentSelectedNode->getInportList();
		vector<NodeOutPort*> outports = _currentSelectedNode->getOutportList();

		QTreeWidget* inportTree = ui.inportTreeWidget;
		QTreeWidget* outportTree = ui.outportTreeWidget;
		QTreeWidget* attributeTree = ui.attributesTreeWidget;

		vector<NodePort*> ports(inports.begin(), inports.end());
		createPortList(inportTree,  &ports);
		createAttributeList(attributeTree, ports);

		ports.assign(outports.begin(), outports.end());
		createPortList(outportTree, &ports);
		
		ui.labelNameIn->setText(_currentSelectedNode->getName().c_str());
		ui.labelNameOut->setText(_currentSelectedNode->getName().c_str());
		ui.nameLabel->setText(_currentSelectedNode->getName().c_str());

		this->_currentSelectedNode->setPortListChanged(false);	// 把portListChanged flag 設成 false
	}
}

// updateMappingNode是一個slot，當treeWidget某個treeItem的checkState改變時就會被呼叫 
// 依照port被checked與否來重畫畫布上 mapping node的 port

void AbstractMappingWindow::updateMappingNode( QTreeWidgetItem* item ,int column )
{
	// 如果沒有點選到item，就直接return
	if( _currentSelectedNode == NULL ){
		ui.attributesTreeWidget->setDisabled(true);
		ui.inportTreeWidget->setDisabled(true);
		ui.outportTreeWidget->setDisabled(true);
		return;
	}
	// 依照port被checked與否來重畫畫布上 mapping node的 port，依照object name來判斷是inport或是outport
	// treeWidget裡面的item被checked

	if ( item->treeWidget()->objectName() == "inportTree") {
		ui.attributesTreeWidget->setDisabled(false);
		ui.inportTreeWidget->setDisabled(false);
		ui.outportTreeWidget->setDisabled(false);

		vector<NodeInPort*>  inportList  = _currentSelectedNode->getInportList();

		for (int i=0; i<inportList.size(); ++i){
			QString* portName = this->curtailPortName(inportList[i]);
			if (item->text(column) == *portName ){
				if (item->checkState(column) == Qt::Checked)
					inportList[i]->setVisible(true);
				else 
					inportList[i]->setVisible(false);
			}
		}
	} else if( item->treeWidget()->objectName() == "outportTree") {
		vector<NodeOutPort*> outportList = _currentSelectedNode->getOutportList();

		for (int i=0; i<outportList.size(); ++i){
			QString* portName = this->curtailPortName(outportList[i]);
			if (item->text(column) == *portName){
				if (item->checkState(column) == Qt::Checked)
					outportList[i]->setVisible(true);
				else 
					outportList[i]->setVisible(false);
			}
		}
	} else {
		assert(false);
	}

	_currentSelectedNode->resetSizeAndPortPosition();	// 調整Node當中port位置以及調整node高度
}

void AbstractMappingWindow::setExpandedState( QTreeWidgetItem* item )
{
	bool isExpand;

	for (int i = 0; i < 7 ; ++i ){
		isExpand = item->treeWidget()->topLevelItem(i)->isExpanded();
		_currentSelectedNode->setTreeItemExpandedState(item->treeWidget()->objectName(),i, isExpand);
	}
}

void AbstractMappingWindow::createPortList( QTreeWidget* portTree, vector<NodePort*>* portList )
{
	assert(portTree);
	assert(portList->size()!=0);

	disconnect( portTree, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( adjustCheckboxState(QTreeWidgetItem* , int)));

	portTree->setHeaderHidden(false);
	portTree->header()->resizeSection(0,129);
	portTree->header()->resizeSection(1,99);

	// 把 QTreeWidget top level 的 children 都清掉
	cleanChildrenOfTreeWidget(portTree);

	QTreeWidgetItem *headerItemName = portTree->findItems("NAME",Qt::MatchExactly).at(0);
	QTreeWidgetItem *headerItemPosition = portTree->findItems("POSITION",Qt::MatchExactly).at(0);
	QTreeWidgetItem *headerItemColor = portTree->findItems("COLOR",Qt::MatchExactly).at(0);
	QTreeWidgetItem *headerItemScale = portTree->findItems("SCALE",Qt::MatchExactly).at(0);
	QTreeWidgetItem *headerItemRotation = portTree->findItems("ROTATION",Qt::MatchExactly).at(0);
	QTreeWidgetItem *headerItemSize = portTree->findItems("SIZE",Qt::MatchExactly).at(0);
	QTreeWidgetItem *headerItemOther = portTree->findItems("OTHER",Qt::MatchExactly).at(0);

	for( int i = 0; i < portList->size(); ++i ){

		NodePort* port = portList->at(i);
		QTreeWidgetItem* treeItem = new QTreeWidgetItem();
		
		QString* portName = curtailPortName(port);
		treeItem->setText(0, QString(trf(portName->toLower().toStdString()).c_str()));

		// 設定 port Class 欄位文字
		setPortClassText(port, treeItem);

		// 設定 port checkbox 值
		if ( port->isVisible() ){
			treeItem->setCheckState(0, Qt::Checked);
		} else {
			treeItem->setCheckState(0, Qt::Unchecked);
		}

		// 將 treeItem 加到對應類別的 parent item 裡
		if ( port->getCategory()==CATEGORY_NAME ) {
			headerItemName->addChild(treeItem);
		} else if ( port->getCategory()==CATEGORY_POSITION ){
			headerItemPosition->addChild(treeItem);
		} else if ( port->getCategory()==CATEGORY_SIZE ){
			headerItemSize->addChild(treeItem);
		} else if ( port->getCategory()==CATEGORY_COLOR ){
			headerItemColor->addChild(treeItem);
		} else if ( port->getCategory()==CATEGORY_SCALE ){
			headerItemScale->addChild(treeItem);
		} else if ( port->getCategory()==CATEGORY_ROTATION ){
			headerItemRotation->addChild(treeItem);
		} else if ( port->getCategory()==CATEGORY_OTHER ){
			headerItemOther->addChild(treeItem);
		}
	}

	// 調整 top level TreeWidget 的 checkbox 可否寫入的狀態
	for ( int i = 0; i < portTree->topLevelItemCount(); ++i ) {
		this->adjustDisableState(portTree->topLevelItem(i));
	}

	connect( portTree, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( adjustCheckboxState(QTreeWidgetItem* , int)));

	// 調整 checkbox 的 check state
	for ( int i = 0; i < portTree->topLevelItemCount(); ++i){
		for ( int j = 0; j < portTree->topLevelItem(i)->childCount(); ++j ) {
			this->adjustCheckboxState(portTree->topLevelItem(i)->child(0), 0);
			break;
		}
	}

	disconnect( portTree, SIGNAL(itemExpanded(QTreeWidgetItem* )), this, SLOT(setExpandedState(QTreeWidgetItem*)));
	disconnect( portTree, SIGNAL(itemCollapsed(QTreeWidgetItem* )), this, SLOT(setExpandedState(QTreeWidgetItem*)));

	headerItemName->setExpanded(_currentSelectedNode->getTreeItemExpandedState(portTree->objectName()).at(CATEGORY_NAME));
	headerItemPosition->setExpanded(_currentSelectedNode->getTreeItemExpandedState(portTree->objectName()).at(CATEGORY_POSITION));
	headerItemColor->setExpanded(_currentSelectedNode->getTreeItemExpandedState(portTree->objectName()).at(CATEGORY_COLOR));
	headerItemScale->setExpanded(_currentSelectedNode->getTreeItemExpandedState(portTree->objectName()).at(CATEGORY_SCALE));
	headerItemRotation->setExpanded(_currentSelectedNode->getTreeItemExpandedState(portTree->objectName()).at(CATEGORY_ROTATION));
	headerItemSize->setExpanded(_currentSelectedNode->getTreeItemExpandedState(portTree->objectName()).at(CATEGORY_SIZE));
	headerItemOther->setExpanded(_currentSelectedNode->getTreeItemExpandedState(portTree->objectName()).at(CATEGORY_OTHER));

	connect( portTree, SIGNAL(itemExpanded(QTreeWidgetItem* )), this, SLOT(setExpandedState(QTreeWidgetItem*)));
	connect( portTree, SIGNAL(itemCollapsed(QTreeWidgetItem* )), this, SLOT(setExpandedState(QTreeWidgetItem*)));

}

void AbstractMappingWindow::createAttributeList( QTreeWidget* portTree, vector<NodePort*>& portList )
{
	assert(portTree);
	assert(portList.size()!=0);

	portTree->setHeaderHidden(false);
	portTree->header()->resizeSection(0,129);
	portTree->header()->resizeSection(1,99);

	// 把 QTreeWidget top level 的 children 都清掉
	cleanChildrenOfTreeWidget(portTree);

	QTreeWidgetItem *headerItemName = portTree->findItems("NAME",Qt::MatchExactly).at(0);
	QTreeWidgetItem *headerItemPosition = portTree->findItems("POSITION",Qt::MatchExactly).at(0);
	QTreeWidgetItem *headerItemColor = portTree->findItems("COLOR",Qt::MatchExactly).at(0);
	QTreeWidgetItem *headerItemScale = portTree->findItems("SCALE",Qt::MatchExactly).at(0);
	QTreeWidgetItem *headerItemRotation = portTree->findItems("ROTATION",Qt::MatchExactly).at(0);
	QTreeWidgetItem *headerItemSize = portTree->findItems("SIZE",Qt::MatchExactly).at(0);
	QTreeWidgetItem *headerItemOther = portTree->findItems("OTHER",Qt::MatchExactly).at(0);

	for( int i = 0; i < portList.size(); ++i ){

		NodePort* port = portList.at(i);
		QTreeWidgetItem* treeItem = new QTreeWidgetItem();

		QString* portName = curtailPortName(port);
		treeItem->setText(0, QString(trf(portName->toLower().toStdString()).c_str()));

		// 設定 port name 欄位文字
		try {
			treeItem->setText(1, dynamic_cast<NodeInPort*>(port)->getPortValue());
		} catch (bad_cast){
			assert(false);
		}

		treeItem->setFlags( treeItem->flags() | Qt::ItemIsEditable);

		if ( port->getCategory()==CATEGORY_NAME ) {
			headerItemName->addChild(treeItem);
		} else if ( port->getCategory()==CATEGORY_POSITION ){
			headerItemPosition->addChild(treeItem);
		} else if ( port->getCategory()==CATEGORY_SIZE ){
			headerItemSize->addChild(treeItem);
		} else if ( port->getCategory()==CATEGORY_COLOR ){
			headerItemColor->addChild(treeItem);
		} else if ( port->getCategory()==CATEGORY_SCALE ){
			headerItemScale->addChild(treeItem);
		} else if ( port->getCategory()==CATEGORY_ROTATION ){
			headerItemRotation->addChild(treeItem);
		} else if ( port->getCategory()==CATEGORY_OTHER ){
			headerItemOther->addChild(treeItem);
		}
	}

	disconnect( portTree, SIGNAL(itemExpanded(QTreeWidgetItem* )), this, SLOT(setExpandedState(QTreeWidgetItem*)));
	disconnect( portTree, SIGNAL(itemCollapsed(QTreeWidgetItem* )), this, SLOT(setExpandedState(QTreeWidgetItem*)));

	headerItemName->setExpanded(_currentSelectedNode->getTreeItemExpandedState(portTree->objectName()).at(CATEGORY_NAME));
	headerItemPosition->setExpanded(_currentSelectedNode->getTreeItemExpandedState(portTree->objectName()).at(CATEGORY_POSITION));
	headerItemColor->setExpanded(_currentSelectedNode->getTreeItemExpandedState(portTree->objectName()).at(CATEGORY_COLOR));
	headerItemScale->setExpanded(_currentSelectedNode->getTreeItemExpandedState(portTree->objectName()).at(CATEGORY_SCALE));
	headerItemRotation->setExpanded(_currentSelectedNode->getTreeItemExpandedState(portTree->objectName()).at(CATEGORY_ROTATION));
	headerItemSize->setExpanded(_currentSelectedNode->getTreeItemExpandedState(portTree->objectName()).at(CATEGORY_SIZE));
	headerItemOther->setExpanded(_currentSelectedNode->getTreeItemExpandedState(portTree->objectName()).at(CATEGORY_OTHER));

	connect( portTree, SIGNAL(itemExpanded(QTreeWidgetItem* )), this, SLOT(setExpandedState(QTreeWidgetItem*)));
	connect( portTree, SIGNAL(itemCollapsed(QTreeWidgetItem* )), this, SLOT(setExpandedState(QTreeWidgetItem*)));

}

DiagramScene* AbstractMappingWindow::currentScene()
{
	return this->_currentScene;
}

void AbstractMappingWindow::adjustDisableState( QTreeWidgetItem* parent )
{
	int requireCount = 0;
	int childrenSize = parent->childCount();

	for ( int i = 0; i < childrenSize; ++i )
	{
		if ( parent->child(i)->text(1) == "REQUIRE" ) {
			requireCount++;
		}
	}
	if ( requireCount == childrenSize ) {
		parent->setDisabled(true);
	} else {
		parent->setDisabled(false);
	}
}

QTabWidget* AbstractMappingWindow::mappingTreeTabWidget()
{
	return this->ui.mappingTreeTabWidget;
}

void AbstractMappingWindow::setCurrentScene( DiagramScene* scene )
{
	this->_currentScene = scene;
}

void AbstractMappingWindow::setCurrentView( QGraphicsView* view )
{
	this->_currentView = view;
}

void AbstractMappingWindow::currentTabChange( int index )
{
	if ( ui.mappingTreeTabWidget->count() != 0 ){
		DiagramScene* scene = findSceneByTabIndex(index);
		assert(scene);
		QGraphicsView* view = scene->views().at(0);

		_currentScene = scene;
		_currentView = view;
		_currentManager = scene->getManager();
	}
}

void AbstractMappingWindow::createNewTab( string tabName )
{
	QWidget* tab = new QWidget();
	QVBoxLayout *layout = new QVBoxLayout;

	// new QGraphicsView
	QGraphicsView* view = new QGraphicsView();
	view->setObjectName("graphicsView");		// 設定object name的用意在於，之後需要用object name來找尋這個物件
											// 請參考resizeEvent()
	//view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
	//view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
	
	AbstractManager *manager = initManager();
	// new QGraphicsScene
	DiagramScene* scene = new DiagramScene(manager);
	scene->setBackgroundBrush(QBrush( QColor(*_canvasBackgroundColor) ));
	

	view->setScene(scene);	
	layout->addWidget(view);
	layout->setSpacing(0);
	layout->setContentsMargins(0,3,0,0);

	tab->setLayout(layout);

	int index = ui.mappingTreeTabWidget->addTab( tab, tabName.c_str());	//將Widget加入TabWidget的Tab中
	ui.mappingTreeTabWidget->setCurrentIndex( index );
	scene->setSceneRect(0,0, SCENE_WIDTH, SCENE_HEIGHT);
	
	// 設定目前使用的view跟scene
	_currentView = view;
	_currentScene = scene;
	view->show();
	

}


// 根據index來找是哪個tab的scene
DiagramScene*  AbstractMappingWindow::findSceneByTabIndex( int index )
{
	QGraphicsView* view = ui.mappingTreeTabWidget->widget(index)->findChild<QGraphicsView*>("graphicsView");
	if ( view == NULL ){
		return NULL;
	}
	DiagramScene* scene =(DiagramScene* )view->scene();

	return scene;
}

// 處裡port名稱字串，如果port名稱前面有_vm_字串則將_vm_拿掉
QString* AbstractMappingWindow::curtailPortName( NodePort* port )
{
	QString* portName;

	if ( port->getName().substr(0,4) == "_vm_" ) {
		portName = new QString ( port->getName().substr(4, port->getName().length()-4).c_str() );
	} else {
		portName = new QString ( port->getName().c_str());
	}
	portName->replace("_", " ");	
	
	return portName;
}


// 設定 port 的屬性欄位文字
void AbstractMappingWindow::setPortClassText( NodePort* port, QTreeWidgetItem* treeItem )
{
	if( port->getClass()==CLASS_REQUIRED ) {
		treeItem->setText(1,"REQUIRE");
		treeItem->setDisabled(true);
	}
	else if( port->getClass()==CLASS_DEFAULT ){
		treeItem->setText(1,"DEFAULT");
	}
	else if( port->getClass()==CLASS_HIDDEN ){
		treeItem->setText(1,"HIDDEN");
	}
}

void AbstractMappingWindow::setPortValue( QTreeWidgetItem* item ,int column )
{
	if (item->treeWidget()->objectName() != "attributeTree") {
		assert(false);
	}

	vector<NodeInPort*>  inportList  = _currentSelectedNode->getInportList();

	for (int i=0; i<inportList.size(); ++i){

		QString* portName = this->curtailPortName(inportList[i]);
		if (item->text(0) == *portName ){
			inportList[i]->getInport()->setDefaultValue(item->text(column).toStdString());
		}
	}
}


void AbstractMappingWindow::adjustCheckboxState( QTreeWidgetItem* item, int column )
{
	disconnect( item->treeWidget(), SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( adjustCheckboxState(QTreeWidgetItem* , int)));

	QTreeWidgetItem* parent = item->parent();

	if ( parent == 0 ) {

		// 這裡的item指的是最上層的tree item (NAME, POSITION, SIZE, COLOR, SCALE, ROTATION, OTHER)
		int childrenSize = item->childCount();
		Qt::CheckState checkState = item->checkState(column);

		if (item->checkState(column) != Qt::PartiallyChecked ) {
			for (int i = 0; i < childrenSize; ++i ){
				if (item->child(i)->text(column+1) != "REQUIRE") {
					item->child(i)->setCheckState(column, checkState );
				} 
				if (item->checkState(column) == Qt::Unchecked && item->child(i)->text(column+1)== "REQUIRE"){
					item->setCheckState(0, Qt::PartiallyChecked);
				}
			}
		}
	}

	else if ( parent != 0 ) {

		// 這裡的item指的是第二層個分類裡面的port item
		int childrenSize = parent->childCount();
		int checkCount = 0;

		// 計算port item@層的item底下的children有幾個被checked
		for (int i = 0; i < childrenSize; ++i ){
			if ( parent->child(i)->checkState(column) == Qt::Checked ){
				checkCount++;
			} 
		}

		if ( checkCount==0 ){						// 如果底下沒有item被checked，checkState 設為 unchecked
			parent->setCheckState(column, Qt::Unchecked);
		} else if ( checkCount == childrenSize ){	// 如果底下item全部被checked，checkState 設為 checked
			parent->setCheckState(column, Qt::Checked );
		} else {									// 如果底下item部分被checked，checkState 設為 partiallyChecked
			parent->setCheckState(column, Qt::PartiallyChecked);
		}
	}	

	connect( item->treeWidget(), SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( adjustCheckboxState(QTreeWidgetItem* , int)));
}

void AbstractMappingWindow::cleanChildrenOfTreeWidget( QTreeWidget* portTree )
{
	for ( int i = 0; i < portTree->topLevelItemCount(); ++i ) {
		if ( portTree->objectName() != "attributeTree" ) {
			portTree->topLevelItem(i)->setCheckState(0, Qt::Unchecked);
		}

		QList<QTreeWidgetItem*> children = portTree->topLevelItem(i)->takeChildren();
		while ( !children.isEmpty() ) {
			delete children.takeFirst();
		}
	}
}

NodeItem* AbstractMappingWindow::currentSelectedNode()
{
	return this->_currentSelectedNode;
}

void AbstractMappingWindow::createOtherWidget()
{
	// 預設 Edit Area 沒有任何其他 Widget
}


void AbstractMappingWindow::resizeEvent( QResizeEvent * event )
{
	// 假如已經存在 _currentScene 跟 _currentView 在重新設定 sceneRect
	if ( _currentScene && _currentView ) {

		for ( int i = 0; i < ui.mappingTreeTabWidget->count(); ++i )
		{
			QGraphicsView *view = dynamic_cast<QGraphicsView*>(ui.mappingTreeTabWidget->widget(i)->layout()->itemAt(0)->widget());
			assert(view);
			QGraphicsScene* scene = view->scene();
			view->scene()->setSceneRect(0,0,scene->width(),scene->height());
		}
	}
	
}

void AbstractMappingWindow::initCanvas()
{
	// 預設不做任何事
}

void AbstractMappingWindow::closeTab( int index )
{
	// 關掉 tab 並且刪除該 tab 裡面的所有物件跟 page widget
	deleteAllByTabIndex(index);
	ui.mappingTreeTabWidget->removeTab( index );
	delete ui.mappingTreeTabWidget->widget(index);

	// 重新設定 currentView 跟 currentScen
	if ( ui.mappingTreeTabWidget->count() != 0 ) {
		int currentIndex = ui.mappingTreeTabWidget->currentIndex();
		_currentScene = findSceneByTabIndex(currentIndex);
		_currentView = _currentScene->views().at(0);
		_currentManager = _currentScene->getManager();
		assert(_currentView);
		assert(_currentScene);
	} 
	// 當 tab 全部都被關掉的時候， cuerrentVie 跟 curretScene 都設成 NULL
	// 並且把 VM 按鈕設成 disable
	else if ( ui.mappingTreeTabWidget->count() == 0 ){
		_currentView = NULL;
		_currentScene = NULL;
		_currentManager = NULL;
		setWidgetDisable(true);
	}
}

// 把所有 VM 按鈕設成 Disable
void AbstractMappingWindow::setVMButtonDisable( bool isDisable)
{
	for ( int i = 0; i < ui.nodeTabWidget->count(); ++i )
	{
		QLayout *layout = ui.nodeTabWidget->widget(i)->layout();

		for ( int j = 0; j < layout->count(); ++j )
		{
			QLayout *vlayout = layout->itemAt(j)->layout();
			QWidget *button = vlayout->itemAt(0)->widget();
			button->setDisabled(isDisable);
		}
	}
}

void AbstractMappingWindow::deleteAllByTabIndex( int index )
{
	DiagramScene *scene = findSceneByTabIndex( index );
	assert(scene);
	QGraphicsView *view = scene->views().at(0);
	assert(view);
	AbstractManager *manager = scene->getManager();
	assert(manager);

	delete view;
	delete scene;
	delete manager;
}

void AbstractMappingWindow::cleanTreeWidget()
{
	disconnect( ui.inportTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( updateMappingNode(QTreeWidgetItem* , int)));
	disconnect( ui.outportTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( updateMappingNode(QTreeWidgetItem* , int)));
	disconnect( ui.attributesTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( setPortValue(QTreeWidgetItem* , int)));
	disconnect( ui.inportTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( adjustCheckboxState(QTreeWidgetItem* , int)));
	disconnect( ui.outportTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( adjustCheckboxState(QTreeWidgetItem* , int)));

	this->cleanChildrenOfTreeWidget(ui.attributesTreeWidget);
	this->cleanChildrenOfTreeWidget(ui.inportTreeWidget);
	this->cleanChildrenOfTreeWidget(ui.outportTreeWidget);
	
	ui.labelNameIn->clear();
	ui.labelNameOut->clear();
	ui.nameLabel->clear();

	connect( ui.inportTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( adjustCheckboxState(QTreeWidgetItem* , int)));
	connect( ui.outportTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( adjustCheckboxState(QTreeWidgetItem* , int)));
	connect( ui.inportTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( updateMappingNode(QTreeWidgetItem* , int)));
	connect( ui.outportTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( updateMappingNode(QTreeWidgetItem* , int)));
	connect( ui.attributesTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem* , int )), this, SLOT( setPortValue(QTreeWidgetItem* , int)));

	this->_currentSelectedNode = NULL;
}

void AbstractMappingWindow::initPortTabWidget()
{
	QTreeWidget* inportTree = ui.inportTreeWidget;
	inportTree->setObjectName("inportTree");
	QTreeWidget* outportTree = ui.outportTreeWidget;
	outportTree->setObjectName("outportTree");
	QTreeWidget* attributeTree = ui.attributesTreeWidget;
	attributeTree->setObjectName("attributeTree");
}

void AbstractMappingWindow::timerEnd()
{
	this->killTimer(_timerId);
}

void AbstractMappingWindow::timerStart()
{
	_currentPressedButton = dynamic_cast<QPushButton*>(sender());
	assert(_currentPressedButton);
	_timerId = startTimer(2000);
}

void AbstractMappingWindow::timerEvent( QTimerEvent* event )
{
	// 跳出 messageBox 提示使用者是否要刪除 VM Button
	QMessageBox msgBox(this);
	msgBox.setText("Do you want to remove this VM button?");
	msgBox.addButton("Remove",QMessageBox::AcceptRole);
	msgBox.addButton("Delete",QMessageBox::YesRole);
	msgBox.addButton("Cancel",QMessageBox::RejectRole);
	msgBox.setIconPixmap (QPixmap(":/resource/bin-red-icon.png"));
	int result = msgBox.exec();
	
	// 如果使用者按下 ok 則刪除該按鈕
	if ( result == 0 ){
		removeVMButton();
		_currentManager->removeVMfromCompositeDes(_currentPressedButton->objectName(), false);
	} else if ( result == 1 )
	{
		removeVMButton();
		_currentManager->removeVMfromCompositeDes(_currentPressedButton->objectName(), true);
	}
	
	// 將timer刪除
	this->killTimer(_timerId);
}

void AbstractMappingWindow::removeVMButton()
{
	int index = ui.nodeTabWidget->currentIndex();
	QLayout *layout = ui.nodeTabWidget->widget(index)->layout();
	QWidget *label = NULL;
	QLayout *vlayout;
	QWidget *button;	

	for ( int i = 0; i < layout->count(); ++i )
	{
		vlayout = layout->itemAt(i)->layout();
		button = vlayout->itemAt(0)->widget();
		if ( button == _currentPressedButton )
		{
			label = vlayout->itemAt(1)->widget();
			break;
		}
	}
	assert(label);
	layout->removeItem(vlayout);
	button->hide();
	label->hide();
}

void AbstractMappingWindow::setWidgetDisable( bool )
{
	setVMButtonDisable(true);
}

